package crp.logic.clustering;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;

import crp.object.SymmMatrix;

import edu.stanford.nlp.math.ArrayMath;
/**
 * Patent cluster
 * @author ouhang
 */
public class NEOClusterP {
	public double[][] featureM = null; //patentNum * kpCenterNum: feature matrix of all patents
	public SymmMatrix corrPM = null;//patentNum*(patentNum+1)/2
	public SymmMatrix distMa = null;
	public SymmMatrix binaryMa = null;
	public double[] cvs = null;
	public double[] mdvs = null;
	public double[] crf = null;
	public int[] centers = null;
	private NEOClusterKP neoClusterKP = null;
	private NEOClusterFactory neoClusterFactory = null;
	
	public NEOClusterP(NEOClusterKP neoClusterKP, NEOClusterFactory neoClusterFactory){
		this.neoClusterKP = neoClusterKP;
		this.neoClusterFactory = neoClusterFactory;
	}
	
	public void cluster(int centerNum) throws Exception{
    	//clustering
    	//preprocessing();
    	//clustering
    	centers = neoClusterFactory.initialCenter(centerNum, distMa, binaryMa, cvs, mdvs, crf, w1, w2);
    	double[] mdvstemp = new double[centers.length];
		double[] cvstemp = new double[centers.length];
		for(int i = 0 ; i < centers.length ; i++) {
			mdvstemp[i] = mdvs[centers[i]];
			cvstemp[i] = mdvs[centers[i]];
		}
		
		//calculate the objective value
		double objective = neoClusterFactory.computeObj(mdvstemp, cvstemp, w1, w2);
    	
    	for(int i = 0 ; i < itr ; i++)
    		objective = neoClusterFactory.clusteringStepreplaceCenter(centers, distMa, cvs, mdvs, crf, w1, w2, objective);
	}
	
	public double computeSimilarity(double dij, double dif){
		return  1 - Math.min(dij,dif)/dif;
	}
	/**
	 * 
	 * @param KPi kwNum * patentNum: feature matrix of all key phrase
	 * @param kp_cp
	 */
	private void computeFeatureM(){
		featureM = new double[neoClusterKP.KPi[0].length][neoClusterKP.centers.length];
		
		for(int i = 0 ; i < neoClusterKP.centers.length ; i++){
			double[] objects = new double[neoClusterKP.binaryMa.dim];//store all kw that belong to center[i]
			for(int j = 0 ; j < neoClusterKP.binaryMa.dim ; j++)
				objects[j] = neoClusterKP.binaryMa.getValue(neoClusterKP.centers[i], j);
			
			double[] temp = new double[featureM.length];
			for(int k = 0 ; k < temp.length ; k++) temp[k] = 0.0;
			for(int k = 0 ; k < objects.length ; k++) 
				if(objects[k] == 1.0)
					temp = ArrayMath.pairwiseAdd(temp, neoClusterKP.KPi[k]);

			for(int j = 0 ; j < featureM.length ; j++)
				featureM[j][i] = temp[j];
		}
	}
	
	private void computeDistM(){
		distMa = new SymmMatrix(featureM.length);
		for(int i = 0 ; i < distMa.dim ; i++){
			//distMa.setValue(NEOClusterFactory.INF, i, i);
			distMa.setValue(0, i, i);
			for(int j = i+1 ; j < distMa.dim ; j++){
				double distance = ArrayMath.norm(ArrayMath.pairwiseSubtract(featureM[i], featureM[j]));
				distMa.setValue(distance, i, j);
			}
		}
	}
	
	private void computeBinaryMa_CorrM(){
		corrPM = new SymmMatrix(distMa.dim);
		
		HashSet<Double> hs = new HashSet<Double>();
		for(int i = 0 ; i < distMa.symM.length ; i++)
			if(distMa.symM[i] != NEOClusterFactory.INF)
				hs.add(distMa.symM[i]);
		ArrayList<Double> list = new ArrayList<Double>(hs);
		Collections.sort(list);
		Double dif = list.get((int) (list.size()-1 - Math.floor(list.size()*0.05)));
		//calculate correlation matrix
		for(int i = 0 ; i < corrPM.dim ; i++){
			corrPM.setValue(0, i, i);
			for(int j = i+1 ; j < distMa.dim ; j++){
				corrPM.setValue(computeSimilarity(distMa.getValue(i, j),dif), i, j);
			}
		}
		
		Ts = neoClusterFactory.calculateTs(corrPM);
		
		//calculate binary matrix
		binaryMa = neoClusterFactory.computeBinaryM(corrPM, Ts);
	}
	
	public void preprocessing(){
		computeFeatureM();
		computeDistM();
		computeBinaryMa_CorrM();
		cvs = new double[distMa.dim];
    	mdvs = new double[distMa.dim];
    	crf = new double[distMa.dim];
	}

	public double Ts = 0;
	public double w1 = 0.5;
	public double w2 = 0.5;
	public int itr = 500;

}
